/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package loadbalancer;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *
 * @author Guilherme
 */
public class LoadBalancer
{
    private HashMap<Integer,Server> servers;
    private int port;
    private ExecutorService pool;
    private int rrb;
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        if (args.length != 2)
        {
            System.err.println("[X] SINTAXE LoadBalancer <Port> <Settings file>");
            return;
        }
        try
        {
            LoadBalancer lb = new LoadBalancer(args);
            lb.run();
        } catch (FileNotFoundException ex)
        {
            System.err.println("[X] Settings file not found");
        } catch (IOException ex)
        {
            System.err.println("[X] I/O Error");
        }
    }
    
    public LoadBalancer(String[] args) throws FileNotFoundException, IOException
    {
        this.port = Integer.parseInt(args[0]);
        this.readServersFile(args[1]);
        this.pool = Executors.newCachedThreadPool();
        this.rrb = 0;
    }
    
    private void readServersFile(String pathName) throws FileNotFoundException, IOException
    {
        BufferedReader br = new BufferedReader(new FileReader(pathName));
        this.servers = new HashMap<>();
        String address;
        int indexServer = 0, i, port;
        for(String s = br.readLine(); s != null; s = br.readLine(), indexServer++)
        {
            for(i = 0; (i < s.length()) && (s.charAt(i) != ' '); i++);
            address = s.substring(0, i);
            port = Integer.parseInt(s.substring(i + 1));
            servers.put(indexServer, new Server(address, port));
        }
        br.close();
    }
    
    public void run()
    {
        ServerSocket socketServidor = null;
        try {
            System.out.println("[$] Port…");
            socketServidor = new ServerSocket(port);
        } catch (IOException e) {
            System.err.println("[X] " + e.getMessage());
            return;
        } catch (Exception e) {
            System.err.println("[X] " + e.getMessage());
            return;
        }
        
        System.out.println("[i] Status: [OK]");
        
        while (true) {
            System.out.println("[i] Waiting");
            try {
                // Aceitando a uma requisição e a redirecionado para uma thread resolver
                pool.submit(new Load(this, socketServidor.accept()));
            } catch (java.io.IOException ex) {
                System.out.println("[X] Connection error: " + ex.getMessage());
                break;
            }
        }
        
    }
    
    public int computeDestinationServer(String key)
    {
        String md5Key = md5(key);
        int sum = 0;
        
        for(int i = 0; i < md5Key.length(); i++)
        {
            sum += md5Key.charAt(i);
        }
        return sum % this.numberOfServers();
    }
    
    public static String md5(String key){  
        String sen = "";  
        MessageDigest md = null;  
        try {  
            md = MessageDigest.getInstance("MD5");  
        } catch (NoSuchAlgorithmException e) {  
            e.printStackTrace();  
        }  
        BigInteger hash = new BigInteger(1, md.digest(key.getBytes()));  
        sen = hash.toString(16); 
        return sen;  
    }
    
    public int numberOfServers()
    {
        return this.servers.size();
    }
    
    public String getAddressServer(int key)
    {
        return this.servers.get(key).getCompleteAddress();
    }
    
    public String getDestinationWebServerAddress()
    {
        return this.servers.get((this.rrb + 1) % this.numberOfServers()).getCompleteAddress();
    }
}
